package io.itit.smartjdbc.provider.where;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.itit.smartjdbc.Query;
import io.itit.smartjdbc.SmartDataSource;
import io.itit.smartjdbc.SmartJdbcException;
import io.itit.smartjdbc.annotations.QueryField;
import io.itit.smartjdbc.cache.CacheManager;
import io.itit.smartjdbc.domain.EntityFieldInfo;
import io.itit.smartjdbc.domain.EntityInfo;
import io.itit.smartjdbc.domain.QueryFieldInfo;
import io.itit.smartjdbc.domain.QueryInfo;
import io.itit.smartjdbc.domain.SmartJdbcCondition;
import io.itit.smartjdbc.domain.SmartJdbcFilter;
import io.itit.smartjdbc.enums.ConditionType;
import io.itit.smartjdbc.enums.SqlOperator;
import io.itit.smartjdbc.util.ArrayUtils;
import io.itit.smartjdbc.util.SmartJdbcUtils;

/**
 * 
 */
public class QueryWhereBuilder {

	private QueryWhere qw;
	private Query<?> query;
	private EntityInfo entity;
	private Class<?> entityClass;
	private SmartDataSource smartDataSource;
	//
	public QueryWhereBuilder(QueryWhere qw, Query<?> query, 
			Class<?> entityClass, SmartDataSource smartDataSource) {
		this.qw=qw;
		this.query=query;
		this.entityClass=entityClass;
		this.smartDataSource=smartDataSource;
		entity=CacheManager.getEntityInfo(entityClass);
	}
	//
	public void build() {
		if(query==null) {
			return;
		}
		QueryInfo queryInfo=CacheManager.getQueryInfo(query.getClass());
		Map<String,Object> paraMap=new HashMap<>();
		if(!query.getParams().isEmpty()) {
			paraMap.putAll(query.getParams());
		}
		createParaMap(paraMap, query, queryInfo);
		addWheres(qw.getWhere(), paraMap, query, queryInfo);
		SmartJdbcFilter filter=query.getFilter();
		if(filter!=null) {
			SmartJdbcFilter rootfilter=new SmartJdbcFilter();
			if(filter.opt==null) {
				filter.opt=SmartJdbcFilter.OPT_AND;
			}
			rootfilter.children.add(filter);
			Map<String,EntityFieldInfo> fieldMap=entity.getFieldMap();
			Map<String,String> fieldColumnMap=new HashMap<>();//key userName value user_name
			fieldMap.forEach((k,v)->{
				fieldColumnMap.put(k, smartDataSource.convertFieldNameToColumnName(entityClass,k));
			});
			addCondtions(fieldColumnMap, qw.getWhere(), rootfilter);
		}
	}
	//
	private void addCondtions(Map<String,String> fieldMap, Where where, SmartJdbcFilter filter) {
		where.conditionType=ConditionType.valueOf(filter.opt.toUpperCase());
		if(filter.conditionList!=null) {
			for (SmartJdbcCondition cond : filter.conditionList) {
				if(cond.fieldId==null||cond.opt==null) {
					continue;
				}
				String fieldColumn=fieldMap.get(cond.fieldId);
				if(fieldColumn==null) {
					throw new RuntimeException("过滤字段不存在"+cond.fieldId);
				}
				String opt=cond.opt;
				if(opt.equals(SmartJdbcCondition.OPT_等于)) {
					where.eq(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_不等于)) {
					where.ne(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_大于)) {
					where.gt(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_大于等于)) {
					where.ge(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_小于)) {
					where.lt(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_小于等于)) {
					where.le(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_包含)) {
					where.like(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_不包含)) {
					where.notLike(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_在列表中)) {
					where.in(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_不在列表中)) {
					where.notin(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_开始是)) {
					where.likeRight(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_结尾是)) {
					where.likeLeft(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_已设置)) {
					where.isNotNull(fieldColumn);
				}
				if(opt.equals(SmartJdbcCondition.OPT_未设置)) {
					where.isNull(fieldColumn);
				}
				if(opt.equals(SmartJdbcCondition.OPT_范围内)) {
					where.betweenAnd(fieldColumn, cond.value);
				}
				if(opt.equals(SmartJdbcCondition.OPT_不在范围内)) {
					where.notBetweenAnd(fieldColumn, cond.value);
				}
			}
		}
		if(filter.children!=null) {
			for (SmartJdbcFilter child : filter.children) {
				Where childWhere=new Where();
				where.addWhere(childWhere);
				addCondtions(fieldMap, childWhere, child);
			}
		}
	}
	//
	@SuppressWarnings("deprecation")
	private void createParaMap(Map<String,Object> paraMap,Object obj,QueryInfo queryInfo) {
		try {
			List<QueryFieldInfo> fields=queryInfo.getFieldList();
			for (QueryFieldInfo info : fields) {
				Field field=info.getField();
				try {
					if(!field.isAccessible()) {
						field.setAccessible(true);
					}
					Object value=field.get(obj);
					paraMap.put(info.getFieldName(), value);
				} catch (Exception e) {
					throw new SmartJdbcException(e.getMessage(),e);
				}
			}
			List<QueryInfo> children=queryInfo.getChildren();
			for (QueryInfo child : children) {
				Field field=child.getField();
				if(!field.isAccessible()) {
					field.setAccessible(true);
				}
				Object childObj=field.get(obj);
				if(childObj==null) {
					continue;
				}
				createParaMap(paraMap, childObj, child);
			}
		} catch (Exception e) {
			throw new IllegalArgumentException(e.getMessage(),e);
		}
		
	}
	//
	@SuppressWarnings("deprecation")
	protected void addWheres(Where w,Map<String,Object> paraMap,Object obj,QueryInfo queryInfo) {
		try {
			List<QueryFieldInfo> fields=queryInfo.getFieldList();
			for (QueryFieldInfo fieldInfo : fields) {
				Field field=fieldInfo.getField();
				if(!field.isAccessible()) {
					field.setAccessible(true);
				}
				Object value=field.get(obj);
				if(value==null) {
					continue;
				}
				if(ArrayUtils.isArrayType(field)&&ArrayUtils.isArrayEmpty(value)) {
					continue;
				}
				if(field.getType().equals(String.class)&&SmartJdbcUtils.isEmpty(value.toString())) {//字符串 忽略空串
					continue;
				}
				QueryField queryField=field.getAnnotation(QueryField.class);
				String alias = fieldInfo.getTableAlias();
				//
				if(queryField!=null&&!SmartJdbcUtils.isEmpty(queryField.whereSql())) {//whereSql check first
					String whereSql=queryField.whereSql();
					w.whereSql(whereSql,paraMap);
				}else {
					String dbFieldName=null;
					if(queryField!=null&&(!SmartJdbcUtils.isEmpty(queryField.field()))) {
						dbFieldName=smartDataSource.convertFieldNameToColumnName(fieldInfo.getEntityClass(),queryField.field());
					}else {
						dbFieldName=smartDataSource.convertFieldNameToColumnName(fieldInfo.getEntityClass(),field.getName());
					}
					SqlOperator operator=SqlOperator.EQ;//default eq
					if(queryField!=null) {
						operator=queryField.operator();
					}
					w.where(alias,dbFieldName,operator,value);
				}
			}
			//
			List<QueryInfo> children=queryInfo.getChildren();
			for (QueryInfo child : children) {
				Field field=child.getField();
				if(!field.isAccessible()) {
					field.setAccessible(true);
				}
				Object childObj=field.get(obj);
				if(childObj==null) {
					continue;
				}
				Where wc=new Where();
				if(child.getConditionType().equals(ConditionType.AND)) {
					w.and(wc);
				}else {
					w.or(wc);
				}
				addWheres(wc, paraMap, childObj, child);
			}
		} catch (Exception e) {
			throw new IllegalArgumentException(e.getMessage(),e);
		}
	}
}
